﻿
using System;
using System.Collections.Generic;
using System.Linq;


namespace Boilen.Primitives {

    public abstract class CodeWriter : ICodeWriter {

        private bool afterNewline_ = true;
        private readonly Stack<string> indents_ = new Stack<string>( );


        public IDisposable PushIndent( string indent ) {
            this.indents_.Push( indent );
            return new Indent( ( ) => this.indents_.Pop( ) );
        }

        public void Write( string value ) {
            this.WriteIndent( false );
            this.WriteCore( value );
        }

        public void Write( string format, params object[] args ) {
            string value = string.Format( format, args );
            this.Write( value );
        }

        public void WriteUnindented( string format, params object[] args ) {
            string prefix = this.afterNewline_ ? "" : Environment.NewLine;
            this.afterNewline_ = false;
            this.Write( prefix + format, args );
        }

        public void WriteLine( ) {
            this.WriteLineCore( "" );
            this.afterNewline_ = true;
        }

        public void WriteLine( string value ) {
            this.WriteIndent( true );
            this.WriteLineCore( value );
        }

        public void WriteLine( string format, params object[] args ) {
            string value = string.Format( format, args );
            this.WriteLine( value );
        }

        public void WriteLineUnindented( string format, params object[] args ) {
            this.WriteUnindented( format, args );
            this.WriteLine( );
        }


        protected abstract void WriteCore( string value );
        protected abstract void WriteLineCore( string value );


        private void WriteIndent( bool willBeAfterNewline ) {
            if( this.afterNewline_ )
                foreach( string indent in this.indents_.Reverse( ) )
                    this.WriteCore( indent );

            this.afterNewline_ = willBeAfterNewline;
        }


        private sealed class Indent : IDisposable {
            private readonly Action dispose_;
            public Indent( Action dispose ) { this.dispose_ = dispose; }
            public void Dispose( ) { this.dispose_( ); }
        }

    }

}
